package com.ifp.opengate.boot.filter;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;

import java.net.URI;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ifp.adapter.common.AdapterCanstants;
import com.ifp.core.connector.AbstractConnector;
import com.ifp.core.connector.AbstractRPCConnector;
import com.ifp.core.context.ClogicContext;
import com.ifp.core.exception.BaseException;
import com.ifp.core.log.Trace;
import com.ifp.core.util.SpringContextsUtil;
import com.ifp.core.util.StringUtil;
import com.ifp.gateway.GatewayConstants;
import com.ifp.opengate.boot.constants.Constants;
import com.ifp.opengate.boot.dao.ApiLogMapper;
import com.ifp.opengate.boot.emum.ErrorEnum;
import com.ifp.opengate.boot.entity.ApiLog;
import com.ifp.opengate.boot.utils.UniErrCodeUtils;
import com.ifp.opengate.boot.utils.WebFluxUtil;

import reactor.core.publisher.Mono;

/**
 * @Classname TcpRoutingFilter
 * @Description TODO
 * @Date 2019/5/27 14:06
 * @Created by zzh
 *          <p>
 *          zhuzh@belink.com
 *          </p>
 * @Version 1.0
 */
@Component
public class EndRoutingFilter implements GlobalFilter, Ordered {

    @Autowired
    private UniErrCodeUtils uniErrCodeUtils;

    @Autowired
    private ApiLogMapper apiLogMapper;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        long reqStartTime = System.currentTimeMillis();;
        long endtime = 0;
        long time = 0;
        // 内部系统编号 如： esb、osb、unionPay等等,用于获取报文模板
        ClogicContext clogicContext = (ClogicContext)exchange.getAttributes().get(Constants.CL_CONTEXT);
        String subscribeSystemId = (String)clogicContext.getTempMap().get(Constants.SUBCRIBE_SYSTEM_ID);
        Map<String, Object> dataMap = clogicContext.getDataMap();
        dataMap.put(Constants.REQUEST_IP, exchange.getAttribute(Constants.REQUEST_IP));
        String consumerId = (String)clogicContext.getTempMap().get(Constants.SUBCRIBE_SERVICE_CODE);
        String connectorCode = (String)exchange.getAttributes().get("connector");
        // TODO:
        // Transaction transaction = deployCatPoint(exchange);
        // exchange.getAttributes().put(Constants.TRANSACTION, transaction);
        String appId = (String)dataMap.get("appId");
        exchange.getAttributes().put(Constants.ISSTATISTICS, false);
        Trace.log(Trace.MODULE_TRACE, Trace.LEVEL_INFO, "EndRoutingFilter");
        if ("5".equals(clogicContext.getTempMap().get("gwTmpKeySandBoxFlag"))) {
            return chain.filter(exchange);
        } else if (connectorCode.contains("HTTPGatewayServiceConnector")) {
            return getHttpGatewayMono(clogicContext, exchange, chain, connectorCode);
        }
        Map<String, Object> headerMapLog = (Map<String, Object>)dataMap.get(AdapterCanstants.HEADER);

        LambdaQueryWrapper<ApiLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApiLog::getRequestId,(String)headerMapLog.get(Constants.REQUEST_ID));
        ApiLog apiLog = new ApiLog();
        if (!ServerWebExchangeUtils.isAlreadyRouted(exchange)) {
            ServerWebExchangeUtils.setAlreadyRouted(exchange);
            Map<String, Object> headerMap = (Map<String, Object>)dataMap.get(AdapterCanstants.HEADER);
            headerMap.put("consumerId", consumerId);
            headerMap.put(Constants.API_NAME, clogicContext.getTempMap().get(Constants.API_NAME));
            // Map<String, Object> bodyMap = (Map<String, Object>)dataMap.get(AdapterCanstants.BODY);
            // clogicContext.getTempMap().putAll(bodyMap);
            // String subscribeProtocol = (String) clogicContext.getTempMap().get(Constants.SUBCRIBE_PROTOCOL);
            // if (!StringUtil.hasText(subscribeProtocol)) {
            // Trace.logError(Trace.MODULE_INTERCEPTOR, "该api[{}]未设置内部协议", appId);
            // throw new AccessControlException(ErrorEnum.noProtocolError.code(),
            // String.format(ErrorEnum.noProtocolError.msg(), appId));
            // }
            String transCode = clogicContext.getTransCode();
            // 行内系统通讯全局流水
            String responseId = (String)headerMap.get(Constants.RESPONSE_ID);
            String requestId = (String)headerMap.get(Constants.REQUEST_ID);
            // 通讯报文头
            Map<String, String> connHeadMap = new HashMap<String, String>();
            connHeadMap.put(GatewayConstants.CHANNEL_ID, subscribeSystemId);

            connHeadMap.put(GatewayConstants.TRANS_CODE, transCode);
            connHeadMap.put(Constants.RESPONSE_ID, responseId);
            String merchantNo = (String)clogicContext.getTempMap().get(Constants.MERCHANT_NO);
            connHeadMap.put(Constants.MERCHANT_NO, merchantNo);
            String recMsg = "";
            // 暂时屏蔽存放无用参数信息 by liulx
            // Map<String, Object> sendMap = new HashMap<>();
            // dataMap.put("requestMap", sendMap);
            try {
                Map<Object, Object> connectorMap = (Map<Object, Object>)exchange.getAttributes().get("connectorMap");
                if (!StringUtil.hasText(connectorCode) || !SpringContextsUtil.containsBean(connectorCode)) {
                    throw new BaseException(ErrorEnum.commError.code(), ErrorEnum.commError.msg());
                }
                dataMap.put("reqId", exchange.getAttributes().get("logId"));
                // sendMap.putAll(connHeadMap);
                // sendMap.putAll(bodyMap);
                // 暂时屏蔽存放无用参数信息 by liulx
                // dataMap.put("requestMap", sendMap);
                // 用于打印日志
                String apiName = (String)clogicContext.getTempMap().get(Constants.API_NAME);
                String protocol = (String)connectorMap.get(connectorCode);
                if ("rpc".equalsIgnoreCase(protocol)) {
                    reqStartTime = System.currentTimeMillis();
                    recMsg = getRpcRecMsg(clogicContext, dataMap, connectorCode, transCode, connHeadMap, apiName);
                    endtime = System.currentTimeMillis();
                    time = endtime - reqStartTime;
                    dataMap.put("time", time);
                    if (!StringUtil.hasText(recMsg)) {
                        throw new BaseException(ErrorEnum.apiServiceError.code(),
                            String.format(ErrorEnum.apiServiceError.msg(), subscribeSystemId + "[" + apiName + "]"));
                    }
                } else {
                    reqStartTime = System.currentTimeMillis();
                    recMsg = getOtherRecMsg(subscribeSystemId, connectorCode, dataMap, apiName);
                    endtime = System.currentTimeMillis();
                    time = endtime - reqStartTime;
                    dataMap.put("time", time);

                    if (!StringUtil.hasText(recMsg)) {
                        throw new BaseException(ErrorEnum.apiServiceError.code(),
                            String.format(ErrorEnum.apiServiceError.msg(), subscribeSystemId + "[" + apiName + "]"));
                    }
                }

                if (!StringUtil.hasText(recMsg)) {
                    throw new BaseException(ErrorEnum.accessIPError.code(), ErrorEnum.accessIPError.msg());
                }
                // 报文错误码映射
                /*
                 * JSONObject data = JSON.parseObject(recMsg); if(data!=null && data.size()>0){
                 * data = errorCodeMappingHandler(transCode, data); }
                 */

                // ================================
                // TODO:
                // if (null != transaction) {
                // transaction.complete();
                // }
                // return WebFluxUtil.writeFlux(exchange, recMsg, chain);
                exchange.getAttributes().put("sofaRequstLog", dataMap);
                HashMap hashMap = JSON.parseObject(recMsg, HashMap.class);
                if(hashMap.get("body")!=null) {
                    HashMap hashMap2 = JSON.parseObject(hashMap.get("body").toString(), HashMap.class);
                    if (hashMap2.get("errorMsg") != null) {
                        apiLog.setErrorMessage(hashMap2.get("errorMsg").toString());
                    }
                }
                apiLog.setResponseTime(LocalDateTime.now());
                apiLog.setIsSuccess(1);
                apiLogMapper.update(apiLog,queryWrapper);
                return WebFluxUtil.writeFlux(exchange, recMsg, chain);
            } catch (Exception ex) {
                // TODO:
                // if (null != transaction||!transaction.isCompleted()) {
                // if (ex instanceof BaseException) {
                // BaseException exception = (BaseException) ex;
                // setExeptionTransaction(exception);
                // } else {
                // BaseException exception = new BaseException(ex);
                // setExeptionTransaction(exception);
                // }
                // transaction.complete();
                // exchange.getAttributes().put(CAT_TRANSACTION,new ArrayList<>());
                // }
                apiLog.setIsSuccess(0);
                apiLog.setErrorMessage(ex.getMessage());
                apiLog.setResponseTime(LocalDateTime.now());
                apiLogMapper.update(apiLog,queryWrapper);
                throw new BaseException(ex);
            }
        } else {
            endtime = System.currentTimeMillis();
            apiLog.setIsSuccess(1);
            apiLog.setResponseTime(LocalDateTime.now());
            apiLogMapper.update(apiLog,queryWrapper);
            time = endtime - reqStartTime;
            dataMap = clogicContext.getDataMap();
            dataMap.put("time", time);
            exchange.getAttributes().put("sofaRequstLog", dataMap);
            return chain.filter(exchange);
        }
    }

    private JSONObject errorCodeMappingHandler(String transCode, JSONObject data) {
        String apiCodeVer = "";
        String errorCode = (String)data.get("errorCode");
        String errorMsg = (String)data.get("errorMsg");
        Object body = data.get("body");
        Object header = data.get("header");
        if (errorCode == null) {
            errorCode = getErrCodeMessageInfo(header, body, "errorCode");
        }
        if (errorMsg == null) {
            errorMsg = getErrCodeMessageInfo(header, body, "errorMsg");
        }
        String apiVersion = getErrCodeMessageInfo(header, body, Constants.VERSION);
        String sysCode = getErrCodeMessageInfo(header, body, Constants.SUBCRIBE_SYSTEM_ID);
        String channel = getErrCodeMessageInfo(header, body, Constants.APP_ID);
        if (StringUtil.hasText(transCode) && StringUtil.hasText(apiVersion)) {
            apiCodeVer = transCode + "_" + apiVersion;
        }
        Map codeInfo = uniErrCodeUtils.getUniErrCodeInfo(errorCode, errorMsg, apiCodeVer, sysCode, channel);
        JSONObject bo = JSON.parseObject(body.toString());
        bo.put(Constants.ERROR_CODE, codeInfo.get(Constants.ERROR_CODE));
        bo.put(Constants.ERROR_MSG, codeInfo.get(Constants.ERROR_MSG));
        JSONObject head = JSON.parseObject(header.toString());
        head.put(Constants.ERROR_CODE, codeInfo.get(Constants.ERROR_CODE));
        head.put(Constants.ERROR_MSG, codeInfo.get(Constants.ERROR_MSG));
        data.put(Constants.ERROR_CODE, codeInfo.get(Constants.ERROR_CODE));
        data.put(Constants.ERROR_MSG, codeInfo.get(Constants.ERROR_MSG));
        data.put("body", bo);
        data.put("header", head);
        return data;
    }

    private String getErrCodeMessageInfo(Object header, Object body, String key) {
        JSONObject head = JSON.parseObject(header.toString());
        JSONObject bo = JSON.parseObject(body.toString());
        String message = (String)head.get(key);
        if (!StringUtil.hasText(message)) {
            message = (String)bo.get(key);
        }
        return message;
    }

    /*private Transaction deployCatPoint(ServerWebExchange exchange) {
    	List<CatTransation> transactions = (List<CatTransation>) exchange.getAttributes().get(CAT_TRANSACTION);
    	if (CollectionUtils.isNotEmpty(transactions)) {
    		CatTransation httpCatTranstion = transactions.remove(0);
    		String logId = (String) exchange.getAttributes().get(com.ifp.openapi.common.Constants.GLOBALLOGID);
    		Cat.getProducer().setCustomizeMsgId(logId.substring(logId.lastIndexOf("-") + 1));
    		Transaction httpTransaction = Cat.getProducer().newTransaction(httpCatTranstion.getTransationName(),
    				httpCatTranstion.getDesc());
    		httpTransaction.setStatus(httpCatTranstion.getStatus());
    		Transaction transaction = Cat.getProducer().newTransaction("Http.Info", logId);
    		transaction.setStatus("0");
    		transaction.complete();
    		for (CatTransation transation : transactions) {
    			Transaction tsn = Cat.newTransaction(transation.getTransationName(), transation.getDesc());
    			tsn.setStatus(transation.getStatus());
    			Map<String, String> events = transation.getEvents();
    			for (Map.Entry<String, String> event : events.entrySet()) {
    				Cat.logEvent(event.getKey(), event.getValue());
    			}
    			tsn.complete();
    		}
    		exchange.getAttributes().put(Constants.CAT_TRANSACTION, new ArrayList<>());
    		return httpTransaction;
    	}
    	return null;
    }
    
    *//**
        * 设置错误信息
        *
        * @param exception
        *//*
          private void setExeptionTransaction(BaseException exception) {
          Transaction t = Cat.newTransaction("Gateway.exception",
          		exception.getErrorCode() + ":" + exception.getErrorMessage());
          t.addData(exception.getErrorCode(), exception.getErrorMessage());
          t.setStatus("0");
          t.complete();
          }*/

    private String getOtherRecMsg(String subscribeSystemId, String connectorCode, Map<String, Object> sendMap,
        String apiName) throws Exception {
        Trace.log("TRACE", Trace.LEVEL_INFO, "{}subscribeSystemId connectorCode {} sendMap: {} apiName: {}",
            subscribeSystemId, connectorCode, sendMap, apiName);
        String recMsg;
        AbstractConnector connector = (AbstractConnector)SpringContextsUtil.getBean(connectorCode);
        Object recObj = connector.sendAndReceive(apiName, subscribeSystemId, sendMap);
        if (recObj instanceof String) {
            recMsg = (String)recObj;
        } else {
            recMsg = JSON.toJSONString(recObj);
        }
        return recMsg;
    }

    private String getRpcRecMsg(ClogicContext clogicContext, Map<String, Object> dataMap, String connectorCode,
        String transCode, Map<String, String> connHeadMap, String apiName) throws Exception {
        String recMsg;
        String consumerId = (String)clogicContext.getTempMap().get(Constants.SUBCRIBE_GROUP);
        connHeadMap.put(GatewayConstants.TRANS_CODE, transCode);

        dataMap.put("tempMap", clogicContext.getTempMap());

        AbstractRPCConnector connector = (AbstractRPCConnector)SpringContextsUtil.getBean(connectorCode);
        connector.setVersion((String)clogicContext.getTempMap().get(Constants.SUBCRIBE_VERSION));
        Object recObj = connector.sendAndReceive(apiName, consumerId, dataMap);
        if (recObj instanceof String) {
            recMsg = (String)recObj;
        } else {
            recMsg = JSON.toJSONString(recObj);
        }
        return recMsg;
    }

    private Mono<Void> getHttpGatewayMono(ClogicContext clogicContext, ServerWebExchange exchange,
        GatewayFilterChain chain, String connectorCode) throws BaseException {
        if (SpringContextsUtil.containsBean(connectorCode)) {
            AbstractConnector abstractConnector = (AbstractConnector)SpringContextsUtil.getBean(connectorCode);
            ServerHttpRequest req = exchange.getRequest();
            String consumerId = (String)clogicContext.getTempMap().get(Constants.SUBCRIBE_SERVICE_CODE);

            URI mergedUrl = URI
                .create("http://" + abstractConnector.getIp() + ":" + abstractConnector.getPort() + "/" + consumerId);
            ServerHttpRequest request = req.mutate().path(mergedUrl.getPath()).build();
            exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);

            Trace.logError(Trace.MODULE_INTERCEPTOR, "http：请求地址[{}],traceCode[[]]", mergedUrl.toString(), consumerId);
            return chain.filter(exchange.mutate().request(request).build());
        } else {
            throw new BaseException(ErrorEnum.accessIPError.code(), ErrorEnum.accessIPError.msg());
        }
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 1;
    }

}
